#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "socket.h"
#include "wizchip_conf.h"
#include "logopng.h"
#include "favico.h"
#include "webpage.h"
#include "httpServer.h"
#include "httpParser.h"
#include "httpUtil.h"
#include "userHandler.h"
#include "loopback.h"

/*****************************************************************************
 * Private types/enumerations/variables
 ****************************************************************************/
static uint8_t HTTPSock_Num[_WIZCHIP_SOCK_NUM_] = {1,2,3};
static st_http_request * http_request;				/**< Pointer to received HTTP request */
static st_http_request * parsed_http_request;		/**< Pointer to parsed HTTP request */
static uint8_t * http_response;						/**< Pointer to HTTP response */

/*****************************************************************************
 * Public types/enumerations/variables
 ****************************************************************************/
uint8_t * pHTTP_TX;
uint8_t * pHTTP_RX;

volatile uint32_t httpServer_tick_1s = 0;
st_http_socket HTTPSock_Status[_WIZCHIP_SOCK_NUM_] = { {STATE_HTTP_IDLE, }, };


/*****************************************************************************
 * Private functions
 ****************************************************************************/
void httpServer_Sockinit(uint8_t cnt, uint8_t * socklist);
static int8_t getHTTPSequenceNum(uint8_t socket);
static int8_t http_disconnect(uint8_t sn);

static void http_process_handler(uint8_t s, st_http_request * p_http_request);
static void send_http_response_header(uint8_t s, uint8_t content_type, uint32_t body_len, uint16_t http_status);
static void send_http_response_body(uint8_t s, uint8_t * uri_name, uint8_t * buf, uint8_t * file_start, uint32_t file_len);
static void send_http_response_cgi(uint8_t s, uint8_t * buf, uint8_t * http_body, uint16_t file_len);

/*****************************************************************************
 * Public functions
 ****************************************************************************/
// Callback functions definition: MCU Reset / WDT Reset
void default_mcu_reset(void) {;}
void default_wdt_reset(void) {;}
void (*HTTPServer_ReStart)(void) = default_mcu_reset;
void (*HTTPServer_WDT_Reset)(void) = default_wdt_reset;

void httpServer_Sockinit(uint8_t cnt, uint8_t * socklist)
{
	uint8_t i;

	for(i = 0; i < cnt; i++)
	{
		// Mapping the H/W socket numbers to the sequential index numbers
		HTTPSock_Num[i] = socklist[i];
	}
}

static int8_t getHTTPSequenceNum(uint8_t socket)
{
	uint8_t i;

	for(i = 0; i < _WIZCHIP_SOCK_NUM_; i++)
		if(HTTPSock_Num[i] == socket) return i;
	return -1;
}

void httpServer_init(uint8_t * tx_buf, uint8_t * rx_buf, uint8_t cnt, uint8_t * socklist)
{
	// User's shared buffer
	pHTTP_TX = tx_buf;
	pHTTP_RX = rx_buf;

	// H/W Socket number mapping
	httpServer_Sockinit(cnt, socklist);
}


/* Register the call back functions for HTTP Server */
void reg_httpServer_cbfunc(void(*mcu_reset)(void), void(*wdt_reset)(void))
{
	// Callback: HW Reset and WDT reset function for each MCU platforms
	if(mcu_reset) HTTPServer_ReStart = mcu_reset;
	if(wdt_reset) HTTPServer_WDT_Reset = wdt_reset;
}

void httpServer_run(uint8_t seqnum)
{
	uint8_t s;	// socket number
	int16_t len;
	uint8_t ret = 0;

#ifdef _HTTPSERVER_DEBUG_
	uint8_t destip[4] = {0, };
	uint16_t destport = 0;
#endif

	http_request = (st_http_request *)pHTTP_RX;		// Structure of HTTP Request
	parsed_http_request = (st_http_request *)pHTTP_TX;

	// Get the H/W socket number
	s = HTTPSock_Num[seqnum];

	/* HTTP Service Start */
	switch(getSn_SR(s))
	{
		case SOCK_ESTABLISHED:
			// Interrupt clear
			if(getSn_IR(s) & Sn_IR_CON)
			{
				setSn_IR(s, Sn_IR_CON);
			}
			// HTTP Process states
			switch(HTTPSock_Status[seqnum].sock_status)
			{
				case STATE_HTTP_IDLE :
					if ((len = getSn_RX_RSR(s)) > 0)
					{
						if (len > DATA_BUF_SIZE) len = DATA_BUF_SIZE;

						// ## 20150828, Eric / Bongjun Hur added
						if ((len = recv(s, (uint8_t *)http_request, len)) < 0) break;	// Exception handler

						////////////////////////////////////////////////////////////////////////////////
						// Todo; User defined custom command handler (userHandler.c)
						ret = custom_command_handler((uint8_t *)http_request);
						////////////////////////////////////////////////////////////////////////////////

						if(ret > 0) // Custom command handler
						{
							// Todo: Users can change this parts for custom function added
							//if(ret == COMMAND_SUCCESS)		send(s, (uint8_t *)"CMDOK", 5);
							//else if(ret == COMMAND_ERROR)	send(s, (uint8_t *)"CMDERROR", 8);
							HTTPSock_Status[seqnum].sock_status = STATE_HTTP_RES_DONE;
						}
						else // HTTP process handler
						{
							*(((uint8_t *)http_request) + len) = '\0';	// End of string (EOS) marker
							parse_http_request(parsed_http_request, (uint8_t *)http_request);
#ifdef _HTTPSERVER_DEBUG_
							getSn_DIPR(s, destip);
							destport = getSn_DPORT(s);
							printf("\r\n");
							printf("> HTTPSocket[%d] : HTTP Request received ", s);
							printf("from %d.%d.%d.%d : %d\r\n", destip[0], destip[1], destip[2], destip[3], destport);
							printf("> HTTPSocket[%d] : [State] STATE_HTTP_REQ_DONE\r\n", s);
#endif
							// HTTP 'response' handler; includes send_http_response_header / body function
							http_process_handler(s, parsed_http_request);

							if(HTTPSock_Status[seqnum].file_len > 0)
								HTTPSock_Status[seqnum].sock_status = STATE_HTTP_RES_INPROC;
							else HTTPSock_Status[seqnum].sock_status = STATE_HTTP_RES_DONE; // Send the 'HTTP response' end
						}
					}
					break;

				case STATE_HTTP_RES_INPROC :
					/* Repeat: Send the remain parts of HTTP responses */
					send_http_response_body(s, 0, http_response, 0, 0);
					if(HTTPSock_Status[seqnum].file_len == 0) 
						HTTPSock_Status[seqnum].sock_status = STATE_HTTP_RES_DONE;
					break;

				case STATE_HTTP_RES_DONE :
#ifdef _HTTPSERVER_DEBUG_
					printf("> HTTPSocket[%d] : [State] STATE_HTTP_RES_DONE\r\n", s);
#endif
					// Socket file info structure re-initialize
					HTTPSock_Status[seqnum].file_len = 0;
					HTTPSock_Status[seqnum].file_offset = 0;
					HTTPSock_Status[seqnum].file_start = 0;
					HTTPSock_Status[seqnum].sock_status = STATE_HTTP_IDLE;
#ifdef _USE_WATCHDOG_
					HTTPServer_WDT_Reset();
#endif
					http_disconnect(s);
					break;
				default :
					break;
			}
			break;

		case SOCK_CLOSE_WAIT:
#ifdef _HTTPSERVER_DEBUG_
		printf("> HTTPSocket[%d] : ClOSE_WAIT\r\n", s);	// if a peer requests to close the current connection
#endif
			disconnect(s);
			break;

		case SOCK_INIT:
			listen(s);
			break;

		case SOCK_LISTEN:
			break;

		case SOCK_SYNSENT:
		case SOCK_SYNRECV:
			break;

		case SOCK_CLOSED:
		default :
#ifdef _HTTPSERVER_DEBUG_
			printf("> HTTPSocket[%d] : CLOSED\r\n", s);
#endif
			if(socket(s, Sn_MR_TCP, HTTP_SERVER_PORT, 0x00) == s)    /* Reinitialize the socket */
			{
#ifdef _HTTPSERVER_DEBUG_
				printf("> HTTPSocket[%d] : OPEN\r\n", s);
#endif
			}
			break;
	} // end of switch
}

////////////////////////////////////////////
// Private Functions
////////////////////////////////////////////
static void send_http_response_header(uint8_t s, uint8_t content_type, uint32_t body_len, uint16_t http_status)
{
	switch(http_status)
	{
		case STATUS_OK: 		// HTTP/1.1 200 OK
			if((content_type != PTYPE_CGI) && (content_type != PTYPE_XML)) // CGI/XML type request does not respond HTTP header
			{
#ifdef _HTTPSERVER_DEBUG_
			printf("> HTTPSocket[%d] : HTTP Response Header - STATUS_OK\r\n", s);
#endif
				make_http_response_head((char*)http_response, content_type, body_len);
			}
			else
			{
#ifdef _HTTPSERVER_DEBUG_
			printf("> HTTPSocket[%d] : HTTP Response Header - NONE / CGI or XML\r\n", s);
#endif
				// CGI/XML type request does not respond HTTP header to client
				http_status = 0;
			}
			break;
		case STATUS_BAD_REQ: 	// HTTP/1.1 400 OK
#ifdef _HTTPSERVER_DEBUG_
			printf("> HTTPSocket[%d] : HTTP Response Header - STATUS_BAD_REQ\r\n", s);
#endif
			memcpy(http_response, ERROR_REQUEST_PAGE, sizeof(ERROR_REQUEST_PAGE));
			break;
		case STATUS_NOT_FOUND:	// HTTP/1.1 404 Not Found
#ifdef _HTTPSERVER_DEBUG_
			printf("> HTTPSocket[%d] : HTTP Response Header - STATUS_NOT_FOUND\r\n", s);
#endif
			memcpy(http_response, ERROR_HTML_PAGE, sizeof(ERROR_HTML_PAGE));
			break;
		default:
			break;
	}

	// Send the HTTP Response 'header'
	if(http_status)
	{
#ifdef _HTTPSERVER_DEBUG_
		printf("> HTTPSocket[%d] : [Send] HTTP Response Header [ %d ]byte\r\n", s, (uint16)strlen((char *)http_response));
#endif
		send(s, http_response, strlen((char *)http_response));
	}
}

static void send_http_response_body(uint8_t s, uint8_t * uri_name, uint8_t * buf, uint8_t * file_start, uint32_t file_len)
{
	int8_t get_seqnum;
	uint32_t send_len;
	uint8_t flag_datasend_end = 0;	
	if((get_seqnum = getHTTPSequenceNum(s)) == -1) 
		return; // exception handling; invalid number
	// Send the HTTP Response 'body'; requested file
	if(!HTTPSock_Status[get_seqnum].file_len) // ### Send HTTP response body: First part ###
	{
		if (file_len > DATA_BUF_SIZE )
		{
			HTTPSock_Status[get_seqnum].file_start = file_start;
			HTTPSock_Status[get_seqnum].file_len = file_len;
			send_len = DATA_BUF_SIZE;
			memset(HTTPSock_Status[get_seqnum].file_name, 0x00, MAX_CONTENT_NAME_LEN);
			strcpy((char *)HTTPSock_Status[get_seqnum].file_name, (char *)uri_name);
#ifdef _HTTPSERVER_DEBUG_
			printf("> HTTPSocket[%d] : HTTP Response body - file name [ %s ]\r\n", s, HTTPSock_Status[get_seqnum].file_name);
			printf("> HTTPSocket[%d] : HTTP Response body - file len [ %ld ]byte\r\n", s, file_len);
#endif
		}
		else
		{
			// Send process end
			send_len = file_len;
#ifdef _HTTPSERVER_DEBUG_
			printf("> HTTPSocket[%d] : HTTP Response end - file len [ %ld ]byte\r\n", s, send_len);
#endif
		}
	}
	else // ### Send HTTP response body: Remained parts ###
	{
		send_len = HTTPSock_Status[get_seqnum].file_len - HTTPSock_Status[get_seqnum].file_offset;

		if(send_len > DATA_BUF_SIZE)
		{
			send_len = DATA_BUF_SIZE;
//#ifdef _HTTPSERVER_DEBUG_
//			printf("> HTTPSocket[%d] : HTTP Response body - send len [ %ld ]byte\r\n", s, send_len);
//#endif
		}
		else
		{
#ifdef _HTTPSERVER_DEBUG_
			printf("> HTTPSocket[%d] : HTTP Response end - file len [ %ld ]byte\r\n", s, HTTPSock_Status[get_seqnum].file_len);
#endif
			// Send process end
			flag_datasend_end = 1;
		}
	}

	// Requested content send to HTTP client
	if(send_len) 
		send(s, HTTPSock_Status[get_seqnum].file_start+HTTPSock_Status[get_seqnum].file_offset, send_len);

	if(flag_datasend_end)
	{
		HTTPSock_Status[get_seqnum].file_start = 0;
		HTTPSock_Status[get_seqnum].file_len = 0;
		HTTPSock_Status[get_seqnum].file_offset = 0;
		flag_datasend_end = 0;
	}
	else
	{
		HTTPSock_Status[get_seqnum].file_offset += send_len;
	}
}

static void send_http_response_cgi(uint8_t s, uint8_t * buf, uint8_t * http_body, uint16_t file_len)
{
	uint16_t send_len = 0;

#ifdef _HTTPSERVER_DEBUG_
	printf("> HTTPSocket[%d] : HTTP Response Header + Body - CGI\r\n", s);
#endif
	send_len = sprintf((char *)buf, "%s%d\r\n\r\n%s", RES_CGIHEAD_OK, file_len, http_body);
#ifdef _HTTPSERVER_DEBUG_
	printf("> HTTPSocket[%d] : HTTP Response Header + Body - send len [ %d ]byte\r\n", s, send_len);
#endif
	send(s, buf, send_len);
}


static int8_t http_disconnect(uint8_t sn)
{
	setSn_CR(sn,Sn_CR_DISCON);
	/* wait to process the command... */
	while(getSn_CR(sn));
	return SOCK_OK;
}


static void http_process_handler(uint8_t s, st_http_request * p_http_request)
{
	uint8_t * uri_name;
	uint8_t * file;
	uint32_t file_len = 0;
	uint8_t post_name[32]={0x00,};	// POST method request file name

#ifdef _WILL_BE_IM_
	uint32_t content_len = 0;
	uint16_t post_len = 0; 			// POST
	uint8_t sub[10];				// POST
#endif

	uint16_t http_status;
	uint8_t content_found = 0;

	if(getHTTPSequenceNum(s) == -1) 
		return; // exception handling; invalid number

	http_status = 0;
	http_response = pHTTP_RX;
	file_len = 0;

	// HTTP Method Analyze
	switch (p_http_request->METHOD)
	{
		case METHOD_ERR :
			http_status = STATUS_BAD_REQ;
			send_http_response_header(s, 0, 0, http_status);
			break;

		case METHOD_HEAD :
		case METHOD_GET :
			uri_name = get_http_uri_name(p_http_request->URI);
			if (!strcmp((char *)uri_name, "/"))
			{
				strcpy((char *)uri_name, INITIAL_WEBPAGE);	// If URI is "/", respond by index.html
				file_len = sizeof(index);
				file = (uint8_t *)index;
			}
			if (!strcmp((char *)uri_name, "m")) 
				strcpy((char *)uri_name, M_INITIAL_WEBPAGE);
			if (!strcmp((char *)uri_name, "mobile")) 
				strcpy((char *)uri_name, MOBILE_INITIAL_WEBPAGE);
			find_http_uri_type(&p_http_request->TYPE, uri_name);	// Checking requested file types (HTML, TEXT, GIF, JPEG and Etc. are included)
			if (strstr((char *)uri_name, "logo"))
			{
				file_len = sizeof(logo);
				file = (uint8_t *)logo;
			}
			if(strstr((char *)uri_name, "favico"))
			{
				file_len = sizeof(favico);
				file = (uint8_t *)favico;
			}
			
#ifdef _HTTPSERVER_DEBUG_
			printf("\r\n> HTTPSocket[%d] : HTTP Method GET\r\n", s);
			printf("> HTTPSocket[%d] : Request Type = %d\r\n", s, p_http_request->TYPE);
			printf("> HTTPSocket[%d] : Request URI = %s\r\n", s, uri_name);
#endif

			if(p_http_request->TYPE == PTYPE_CGI)
			{
				content_found = http_get_cgi_handler(uri_name, pHTTP_TX, &file_len);
				if(content_found && (file_len <= (2048-(strlen(RES_CGIHEAD_OK)+8))))
				{
					send_http_response_cgi(s, http_response, pHTTP_TX, (uint16_t)file_len);
				}
				else
				{
					send_http_response_header(s, PTYPE_CGI, 0, STATUS_NOT_FOUND);
				}
			}
			else
			{	// If No CGI request, Try to find The requested web content in storage (e.g., 'SD card' or 'Data flash')
				if(file_len)
					content_found = 1;
				//find request content and set http_status,here is not need temporary
				if(!content_found)
				{
					http_status = STATUS_NOT_FOUND;
				}
				else
				{
					http_status = STATUS_OK;
				}

				// Send HTTP header
				if(http_status)
				{
#ifdef _HTTPSERVER_DEBUG_
					printf("> HTTPSocket[%d] : Requested content len = [ %ld ]byte\r\n", s, file_len);
#endif
					send_http_response_header(s, p_http_request->TYPE, file_len, http_status);
				}

				// Send HTTP body (content)
				if(http_status == STATUS_OK)
				{
					 send_http_response_body(s, uri_name, http_response, file, file_len);
				}
			}
			break;

		case METHOD_POST :
			mid((char *)p_http_request->URI, "/", " HTTP", (char *)post_name);
			uri_name = post_name;
			find_http_uri_type(&p_http_request->TYPE, uri_name);	//Check file type (HTML, TEXT, GIF, JPEG are included)

#ifdef _HTTPSERVER_DEBUG_
			printf("\r\n> HTTPSocket[%d] : HTTP Method POST\r\n", s);
			printf("> HTTPSocket[%d] : Request URI = %s ", s, post_name);
			printf("Type = %d\r\n", p_http_request->TYPE);
#endif

			if(p_http_request->TYPE == PTYPE_CGI)	// HTTP POST Method; CGI Process
			{
				content_found = http_post_cgi_handler(post_name, p_http_request, http_response, &file_len);
#ifdef _HTTPSERVER_DEBUG_
				printf("> HTTPSocket[%d] : [CGI: %s] / Response len [ %ld ]byte\r\n", s, content_found?"Content found":"Content not found", file_len);
#endif
				if(content_found && (file_len <= (2048-(strlen(RES_CGIHEAD_OK)+8))))
				{
					send_http_response_cgi(s, pHTTP_TX, http_response, (uint16_t)file_len);

					// Reset the H/W for apply to the change configuration information
					if(content_found == HTTP_RESET) HTTPServer_ReStart();
				}
				else
				{
					send_http_response_header(s, PTYPE_CGI, 0, STATUS_NOT_FOUND);
				}
			}
			else	// HTTP POST Method; Content not found
			{
				send_http_response_header(s, 0, 0, STATUS_NOT_FOUND);
			}
			break;

		default :
			http_status = STATUS_BAD_REQ;
			send_http_response_header(s, 0, 0, http_status);
			break;
	}
}

void httpServer_time_handler(void)
{
	httpServer_tick_1s++;
}

uint32_t get_httpServer_timecount(void)
{
	return httpServer_tick_1s;
}
